package kz.kstu.scantest.desktop.api;

import kz.kstu.scantest.business.common.helper.FormDataHelper;
import kz.kstu.scantest.business.common.util.comparator.FormDataComparator;
import kz.kstu.scantest.business.common.util.comparator.FormDataErrorComparator;
import kz.kstu.scantest.business.common.util.comparator.FormDataFifthSubjectComparator;
import kz.kstu.scantest.business.common.util.comparator.FormDataSumOfBallComparator;
import kz.kstu.scantest.business.entity.data.*;
import kz.kstu.scantest.business.entity.data.dto.FormDataDTO;
import kz.kstu.scantest.business.entity.data.FieldNameEnum;
import kz.kstu.scantest.business.entity.data.SubjectEnum;
import kz.kstu.scantest.business.util.StringUtil;
import org.apache.log4j.Logger;

import javax.swing.table.AbstractTableModel;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author Filipp_Stankevich
 *         created: 11.09.11 15:28
 */
public class ScanResultTableModel extends AbstractTableModel implements Sortable {

    private Logger LOGGER = Logger.getLogger(ScanResultTableModel.class);

    private final static Integer UP = 1;

    private final static Integer DOWN = 2;

    private static final String[] COLUMN_NAMES = new String[]{"№", "Фамилия", "И.О.", "Лит. кл.", "Вариант",
            "Пятый предмет", "Бал 1", "Бал 2", "Бал 3", "Бал 4", "Бал 5", "Сумма", "Ошибка"};

    public static final int NUMBER_COLUMN_INDEX = 0;
    public static final int SURNAME_COLUMN_INDEX = 1;
    public static final int INITIALS_COLUMN_INDEX = 2;
    public static final int CLASS_LETTER_COLUMN_INDEX = 3;
    public static final int VARIANT__COLUMN_INDEX = 4;
    public static final int FIFTH_SUBJECT_COLUMN_INDEX = 5;
    public static final int FIRST_BALL_COLUMN_INDEX = 6;
    public static final int SECOND_BALL_COLUMN_INDEX = 7;
    public static final int THIRD_BALL_COLUMN_INDEX = 8;
    public static final int FOURTH_BALL_COLUMN_INDEX = 9;
    public static final int FIFTH_BALL_COLUMN_INDEX = 10;
    public static final int SUM_OF_BALL_COLUMN_INDEX = 11;
    public static final int ERROR_COLUMN_INDEX = 12;

    private LinkedHashMap<Long, FormData> formDataMap = new LinkedHashMap<Long, FormData>();

    private Map<Integer, Integer> columnSortStateMap = new HashMap<Integer, Integer>();

    public ScanResultTableModel() {

    }

    public void addRow(FormData formData) {
        if (!formDataMap.containsKey(formData.getId())) {
            formDataMap.put(formData.getId(), formData);
            fireTableDataChanged();
        } else {
            throw new IllegalArgumentException("Table already contains formData with id = " + formData.getId());
        }
    }

    public void updateRow(FormData formData) {
        if (formDataMap.containsKey(formData.getId())) {
            formDataMap.put(formData.getId(), formData);
            fireTableDataChanged();
        } else {
            throw new IllegalArgumentException("Table not contains formData with id = " + formData.getId());
        }
    }

    public FormData getFormData(int rowIndex) {
        return formDataMap.values().toArray(new FormData[formDataMap.values().size()])[rowIndex];
    }

    public void removeFormData(FormData formData) {
        formDataMap.remove(formData.getId());
        fireTableDataChanged();
    }

    public void updateFormTable(List<FormData> formDataList) {
        formDataMap.clear();
        for(FormData formData : formDataList) {
            formDataMap.put(formData.getId(), formData);
        }
        fireTableDataChanged();
    }

    @Override
    public int getRowCount() {
        return formDataMap.size();
    }

    @Override
    public int getColumnCount() {
        return COLUMN_NAMES.length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {

        Collection<FormData> formDataCollection = formDataMap.values();
        FormData[] formDataArray = formDataCollection.toArray(new FormData[formDataCollection.size()]);
        FormData formData = formDataArray[rowIndex];
        FormDataDTO formDataDTO = FormDataHelper.convertToDTO(formData);

        Object value = null;

        switch (columnIndex) {
            case NUMBER_COLUMN_INDEX:
                value = rowIndex + 1;
                break;
            case SURNAME_COLUMN_INDEX:
                value = formData.getSurname();
                break;
            case INITIALS_COLUMN_INDEX:
                value = formData.getInitials();
                break;
            case CLASS_LETTER_COLUMN_INDEX:
                value = formData.getClassLetter();
                break;
            case VARIANT__COLUMN_INDEX:
                value = formData.getVariant();
                break;
            case FIFTH_SUBJECT_COLUMN_INDEX:
                value = FormDataHelper.getFifthSubjectName(formData);
                break;
            case FIRST_BALL_COLUMN_INDEX:
                value = formDataDTO.getFirstBall();
                break;
            case SECOND_BALL_COLUMN_INDEX:
                value = formDataDTO.getSecondBall();
                break;
            case THIRD_BALL_COLUMN_INDEX:
                value = formDataDTO.getThirdBall();
                break;
            case FOURTH_BALL_COLUMN_INDEX:
                value = formDataDTO.getFourthBall();
                break;
            case FIFTH_BALL_COLUMN_INDEX:
                value = formDataDTO.getFifthBall();
                break;
            case SUM_OF_BALL_COLUMN_INDEX:
                value = formDataDTO.getSumBall();
                break;
            case ERROR_COLUMN_INDEX:
                value = formData.getErrorMessage();
                break;
        }
        return value;
    }

    @Override
    public String getColumnName(int columnIndex) {
        return COLUMN_NAMES[columnIndex];
    }

    public void clear() {
        int size = formDataMap.size();
        if (size > 0) {
            formDataMap.clear();
            fireTableDataChanged();
        }
    }

    public List<FormData> getFormDataList() {
        FormData[] formDataArray =  formDataMap.values().toArray(new FormData[formDataMap.size()]);
        return Arrays.asList(formDataArray);
    }

    private int getFormDataIndex(FormData formData) {
        Collection<FormData> formDataCollection =  formDataMap.values();
        FormData[] formDataArray  = formDataCollection.toArray(new FormData[formDataMap.size()]);
        List<FormData> formDataList = Arrays.asList(formDataArray);
        return formDataList.indexOf(formData);
    }

    @Override
    public void sortByColumn(int columnIndex) {
        Integer sortMode = columnSortStateMap.get(columnIndex);
        boolean isAscending = sortMode == null || (sortMode.equals(DOWN));
        columnSortStateMap.put(columnIndex, isAscending ? UP : DOWN);

        Comparator<FormData> comparator;
        switch (columnIndex) {
            case SURNAME_COLUMN_INDEX:
                comparator  = new FormDataComparator(StringUtil.getGetterByProperty(FormData.SURNAME_PROPERTY), isAscending);
                break;
            case VARIANT__COLUMN_INDEX:
                comparator  = new FormDataComparator(StringUtil.getGetterByProperty(FormData.VARIANT_PROPERTY), isAscending);
                break;
            case FIFTH_SUBJECT_COLUMN_INDEX:
                comparator = new FormDataFifthSubjectComparator(isAscending);
                break;
            case SUM_OF_BALL_COLUMN_INDEX:
                comparator = new FormDataSumOfBallComparator(isAscending);
                break;
            case ERROR_COLUMN_INDEX:
                comparator = new FormDataErrorComparator(isAscending);
                break;
            default:
                return;
        }
        List<FormData> formDataList = new ArrayList<FormData>(formDataMap.values());
        Collections.sort(formDataList, comparator);
        clear();
        addRows(formDataList);
    }

    public void addRows(List<FormData>  formDataList) {
        for (FormData formData : formDataList) {
            formDataMap.put(formData.getId(), formData);
        }
        fireTableDataChanged();
    }
}
